package com.freewater.javabase.demo.crypto;

import javax.crypto.Cipher;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.X509Certificate;

/**
 * 数字证书：我们知道，摘要算法用来确保数据没有被篡改，非对称加密算法可以对数据进行加解密，签名算法可以确保数据完整性和抗否认性，把这些算法集合到一起，并搞一套完善的标准，这就是数字证书。
 */
public class CertCrypto {
    public static void main(String[] args) throws Exception {
        String message = "Hello, World!";
        String password = "123456";
        // 读取KeyStore
        KeyStore keyStore = loadKeyStore("/my.keystore", password);
        System.out.println(keyStore);
        // 读取私匙
        PrivateKey privateKey = (PrivateKey) keyStore.getKey("mycert", password.toCharArray());
        System.out.println(privateKey);
        // 读取证书
        X509Certificate certificate = (X509Certificate) keyStore.getCertificate("mycert");
        System.out.println(certificate);
        // 加密
        byte[] encrypt = encrypt(certificate, message.getBytes(StandardCharsets.UTF_8));
        System.out.println("Encrypted: " + new BigInteger(1, encrypt).toString(16));
        // 解密
        byte[] decrypt = decrypt(privateKey, encrypt);
        System.out.println("Decrypted: " + new String(decrypt, StandardCharsets.UTF_8));
        // 签名
        byte[] sign = sign(privateKey, certificate, message.getBytes(StandardCharsets.UTF_8));
        System.out.println("Sign: " + new BigInteger(1, sign).toString(16));
        // 验证签名
        boolean verify = verify(certificate, message.getBytes(StandardCharsets.UTF_8), sign);
        System.out.println("Verify: " + verify);
    }

    static KeyStore loadKeyStore(String keystoreFile, String password) {
        try (InputStream resourceAsStream = CertCrypto.class.getResourceAsStream(keystoreFile)) {
            if (resourceAsStream == null) {
                throw new RuntimeException("keystore file not found: " + keystoreFile);
            }
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(resourceAsStream, password.toCharArray());
            return keyStore;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    static byte[] encrypt(X509Certificate certificate, byte[] input) throws Exception {
        PublicKey publicKey = certificate.getPublicKey();
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encrypted = cipher.doFinal(input);
        return encrypted;
    }

    static byte[] decrypt(PrivateKey privateKey, byte[] input) throws Exception {
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decrypted = cipher.doFinal(input);
        return decrypted;
    }

    static byte[] sign(PrivateKey privateKey, X509Certificate certificate, byte[] input) throws Exception {
        Signature signature = Signature.getInstance(certificate.getSigAlgName());
        signature.initSign(privateKey);
        signature.update(input);
        return signature.sign();
    }

    static boolean verify(X509Certificate certificate, byte[] message, byte[] sign) throws Exception {
        Signature signature = Signature.getInstance(certificate.getSigAlgName());
        signature.initVerify(certificate.getPublicKey());
        signature.update(message);
        return signature.verify(sign);
    }
}
